﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Clipping.Infrastructure.Interfaces;
namespace Clipping.HtmlDownloaders
{
	public class CommonHtmlDownloader : IHtmlDownload
	{
		private static string _userAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
		private static string _referUrl;
		private static CookieContainer _defaultCookieContainer = new CookieContainer();
		private CookieContainer _cookieContainer;
		public CommonHtmlDownloader(bool enableCookie=true,CookieContainer cookieContainer = null)
		{
			if (enableCookie)
			{
				if (cookieContainer != null)
					_cookieContainer = cookieContainer;
				else
					_cookieContainer = _defaultCookieContainer;
			}
			
		}
		
		/// <summary>
		/// 获取指定地址的HTML，自动检测编码
		/// </summary>
		/// <param name="address"></param>
		/// <returns></returns>
		public string GetHtml(Uri address)
		{
			if (address == null)
				return null;
			string rtn = null;
			HttpWebRequest webrequest = null;
			HttpWebResponse webreponse = null;
			Stream stream = null;

			try
			{
				webrequest = (HttpWebRequest)WebRequest.Create(address);
				webrequest.UserAgent = _userAgent;
				webrequest.Referer = _referUrl;
				webrequest.CookieContainer = _cookieContainer;
				webreponse = (HttpWebResponse)webrequest.GetResponse();
				stream = webreponse.GetResponseStream();

				Byte[] bytes;
				using (BinaryReader br = new BinaryReader(stream))
				{
					int length = 500000;
					if (webreponse.ContentLength > 0)
						length = (int)webreponse.ContentLength;
					bytes = br.ReadBytes(length);
					Encoding encoding = detectEncoding(bytes, Encoding.GetEncoding("GB18030"));
					rtn = encoding.GetString(bytes);
				}
				_referUrl = address.OriginalString;
			}
			finally
			{
				if (webreponse != null)
					webreponse.Close();
				if (stream != null)
					stream.Close();
			}
			return rtn;
		}



		/// <summary>
		/// 检测编码
		/// </summary>
		/// <param name="bytes"></param>
		/// <param name="defaultEncoding"></param>
		/// <returns></returns>
		private Encoding detectEncoding(byte[] bytes, Encoding defaultEncoding)
		{

			int length = bytes.Length;
			if (length >= 2)
			{

				if (length >= 4)
				{
					//UTF32，Big-Endian
					if (isByteArrayStartWith(bytes, 4, 0x00, 0x00, 0xFE, 0xFF))
						return new UTF32Encoding(true, true);
					//UTF32，Little-Endian
					if (isByteArrayStartWith(bytes, 4, 0xFF, 0xFE, 0x00, 0x00))
						return new UTF32Encoding(false, true);
				}
				//UTF8
				if (length >= 3 && isByteArrayStartWith(bytes, 3, 0xEF, 0xBB, 0xBF))
					return new UTF8Encoding(true);

				//UTF16，Big-Endian
				if (isByteArrayStartWith(bytes, 2, 0xFE, 0xFF))
					return new UnicodeEncoding(true, true);
				//UTF16，Little-Endian
				if (isByteArrayStartWith(bytes, 2, 0xFF, 0xFE))
					return new UnicodeEncoding(false, true);


				string str = defaultEncoding.GetString(bytes);
				var match = Regex.Match(str, "<meta\\s[\\s\\S]*?charset\\s*=\\s*([^\"'>\\s]*)[^>]*>", RegexOptions.IgnoreCase);
				if (match.Success)
				{
					string encoding = match.Groups[1].Value;
					if (!string.IsNullOrEmpty(encoding))
					{
						try
						{
							return Encoding.GetEncoding(encoding);
						}
						catch
						{
							return defaultEncoding;
						}
					}
				}


			}
			return defaultEncoding;
		}
		
		/// <summary>
		/// 判断字节数组是否以指定字节开始
		/// </summary>
		/// <param name="bytes"></param>
		/// <param name="count"></param>
		/// <param name="values"></param>
		/// <returns></returns>
		static bool isByteArrayStartWith(byte[] bytes, int count, params int[] values)
		{
			for (int i = 0; i < count; i++)
				if (bytes[i] != values[i])
					return false;
			return true;
		}
		
		public string GetHtml(string address)
		{
			Uri uri = new Uri(address);
			return GetHtml(uri);
		}

		public string GetHtml(Uri address, Encoding encoding)
		{
			if (address == null)
				return null;
			//Contract.Requires(address != null);
			if (encoding != null)
			{
				string rtn = null;
				HttpWebRequest webrequest = null;
				HttpWebResponse webreponse = null;
				Stream stream = null;
				StreamReader sreader;

				try
				{
                    webrequest = (HttpWebRequest)WebRequest.Create(address);
					webrequest.UserAgent = _userAgent;
                    webrequest.Referer = _referUrl;
                    webrequest.CookieContainer = _cookieContainer;
                    webreponse = (HttpWebResponse)webrequest.GetResponse();
                    stream = webreponse.GetResponseStream();
					sreader = new StreamReader(stream, encoding);
					rtn = sreader.ReadToEnd();
					_referUrl = address.OriginalString;
				}
				finally
				{
					if (webreponse != null)
						webreponse.Close();
					if (stream != null)
						stream.Close();
				}
				return rtn;
			}
			else
			{
				return GetHtml(address);
			}
		}

		public string GetHtml(string address, Encoding encoding)
		{
			Uri uri = new Uri(address);
			return GetHtml(uri, encoding);

		}
	}
}
